﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using Cloak;
using Cloak.Globalization;

namespace Ruleweaver.Conditions
{
	public sealed class ConditionKey : IEquatable<ConditionKey>
	{
		[Pure]
		public static bool operator ==(ConditionKey x, ConditionKey y)
		{
			return Object.ReferenceEquals(x, y)
				|| (!Object.ReferenceEquals(x, null) && !Object.ReferenceEquals(y, null) && x.Equals(y));
		}

		[Pure]
		public static bool operator !=(ConditionKey x, ConditionKey y)
		{
			return !(x == y);
		}

		[Pure]
		public static bool IsDefaultName(string name)
		{
			return NameEqualityComparer.Equals(name, DefaultName);
		}

		[Pure]
		public static bool IsInvariantName(string name)
		{
			return NameEqualityComparer.Equals(name, InvariantName);
		}

		public const string DefaultName = "";

		public const string InvariantName = "*";

		public static readonly IEqualityComparer<string> NameEqualityComparer = StringComparer.InvariantCulture;

		public ConditionKey(Type targetType, string name)
		{
			Contract.Requires(targetType != null);
			Contract.Requires(name != null);

			TargetType = targetType;
			Name = name;
		}

		public ConditionKey(Type targetType) : this(targetType, DefaultName)
		{}

		#region Overrides : Object

		public override bool Equals(object obj)
		{
			return Equals(obj as ConditionKey);
		}

		public override int GetHashCode()
		{
			return HashCode.Combine(TargetType, Name);
		}

		public override string ToString()
		{
			return Resources.ConditionKey.FormatInvariant(TargetType.FullName, Name);
		}
		#endregion

		#region IEquatable

		public bool Equals(ConditionKey other)
		{
			return other != null && TargetType == other.TargetType && NameEquals(other.Name);
		}
		#endregion

		public Type TargetType { get; private set; }

		public string Name { get; private set; }

		public bool IsDefault
		{
			get { return IsDefaultName(Name); }
		}

		public bool IsInvariant
		{
			get { return IsInvariantName(Name); }
		}

		public bool NameEquals(string name)
		{
			return NameEqualityComparer.Equals(Name, name);
		}

		[Pure]
		public bool AppliesTo(Type targetType)
		{
			Contract.Requires(targetType != null);

			return TargetType.IsAssignableFrom(targetType);
		}

		[Pure]
		public bool AppliesTo(object target)
		{
			return AppliesTo(target == null ? typeof(object) : target.GetType());
		}
	}
}